#include "FreeWrite.h"
#include "FontMap.h"

namespace freewrite
{

void AllocMap2D::Reset()
{
	if(m_UpperMip)
	{
		m_UsingHead = -1;
		m_FreeHead = -1;
		for(int16_t i=0; i<(int16_t)m_Nodes.size(); i++)
		{
			m_Nodes[i].Next = -1;
			m_Nodes[i].State = ANS_UnAllocated;
		}
	}
	else
	{
		m_UsingHead = -1;
		m_FreeHead = 0;
		for(int16_t i=0; i<(int16_t)m_Nodes.size(); i++)
		{
			m_Nodes[i].Next = i + 1;
			m_Nodes[i].State = ANS_Free;
		}
		m_Nodes[m_Nodes.size() - 1].Next = -1;
	}
}

AllocNode* AllocMap2D::At(int16_t index)
{
	if(index < 0 || (size_t)index > m_Nodes.size()) 
		return nullptr; 
	else 
		return &m_Nodes[index];
}

bool AllocMap2D::Alloc(int16_t& outIndex)
{
	AllocNode* freeNode = At(m_FreeHead);
	if(!freeNode)
	{
		//Alloc from upper mip if exists:
		if(m_UpperMip)
		{
			int16_t upperIndex;
			if(!m_UpperMip->Alloc(upperIndex))
			{
				return false;
			}
			else
			{
				Vector2I16 upperXY = m_UpperMip->IndexToXY(upperIndex);
				NodeAlloc(XYToIndex(upperXY.X * 2, upperXY.Y * 2));
				NodeAlloc(XYToIndex(upperXY.X * 2 + 1, upperXY.Y * 2));
				NodeAlloc(XYToIndex(upperXY.X * 2, upperXY.Y * 2 + 1));
				NodeAlloc(XYToIndex(upperXY.X * 2 + 1, upperXY.Y * 2 + 1));
			}
		}
		else
		{
			return false;
		}
	}
	
	outIndex = NodeUsing();
	assert(outIndex != -1);
	return true;
}

void AllocMap2D::NodeAlloc(int16_t index)
{
	AllocNode* node = At(index);
	assert(node->State == ANS_UnAllocated && node->Next == -1);
	
	node->Next = m_FreeHead;
	m_FreeHead = index;
	node->State = ANS_Free;
}

int16_t AllocMap2D::NodeUsing()
{
	assert(m_FreeHead != -1);
	AllocNode* node = At(m_FreeHead);
	assert(node && node->State == ANS_Free);

	int16_t newFreeHead = node->Next;
	node->Next = m_UsingHead;
	m_UsingHead = m_FreeHead;
	node->State = ANS_Using;
	m_FreeHead = newFreeHead;
	return m_UsingHead;
}

Vector2I16 AllocMap2D::IndexToXY(int16_t index) const
{
	Vector2I16 ret;
	ret.X = index % m_Dim;
	ret.Y = index / m_Dim;
	return ret;
}

int16_t AllocMap2D::XYToIndex(int16_t x, int16_t y) const
{
	return m_Dim * y + x;
}

Vector2I16 AllocMap2D::XYToTexelOffset(int16_t x, int16_t y) const
{
	int16_t texelPerGrid = FW_TEXTURE_DIM / m_Dim;
	Vector2I16 ret;
	ret.X = x * texelPerGrid;
	ret.Y = y * texelPerGrid;
	return ret;
}

}